<?php
/**
 * The MIT License (MIT)
 * Copyright © 2021 Walkline Wang (https://walkline.wang)
 * https://gitee.com/walkline/online-update-manager
 */	

	header('Content-Type: application/json');

	include_once('connect2db.php');
	include_once('exception.php');
	include_once('query_list.php');
	
	error_reporting(E_ALL);
	ini_set("display_errors", 1);

	session_set_cookie_params(24 * 3600 * 365, '/');
	session_start();

	$upload_folder = '/upload'; // 上传资源文件默认路径
	
	$return_object = (object) array();
	$exception = new API_Exception();

	if ($_SERVER['REQUEST_METHOD'] === 'POST') {
		switch ($_REQUEST['v1']) {
			case 'signin_user':
				$return_object = signin_user(@$_POST['username'], @$_POST['password']);
				break;
			case 'load_packages':
				$return_object = _validate_user_authorize() ? load_packages() : $exception->get_response_object(1002);
				break;
			case 'load_apps':
				$return_object = _validate_user_authorize() ? load_apps(@$_POST['package_name']) : $exception->get_response_object(1002);
				break;
			case 'append_package':
				$return_object = _validate_user_authorize() ? append_package(@$_POST['package_name']): $exception->get_response_object(1002);
				break;
			case 'remove_package':
				$return_object = _validate_user_authorize() ? remove_package(@$_POST['package_name']) : $exception->get_response_object(1002);
				break;
			case 'check_upload_file':
				$return_object = _validate_user_authorize() ? check_upload_file(@$_POST['package_name'], @$_POST['filename'], @$_POST['sub_folder']) : $exception->get_response_object(1002);
				break;
			// case 'upload_app':
			// 	$return_object = upload_app(@$_POST['params'], @$_FILES);
			// 	break;
			case 'upload_app_apart':
				$return_object = _validate_user_authorize() ? upload_app_apart(@$_POST['append_data']) : $exception->get_response_object(1002);
				break;
			case 'remove_app':
				$return_object = _validate_user_authorize() ? remove_app(@$_POST['package_name'], @$_POST['include_file'], @$_POST['row_data']) : $exception->get_response_object(1002);
				break;
			case 'check_update':
				$return_object = check_update(@$_POST['package_name'], @$_POST['app_version'], @$_POST['get_last_version']);
				break;
			case 'logout_user':
				$return_object = _validate_user_authorize() ? logout_user() : $exception->get_response_object(1002);
				break;
			default:
				$return_object = $exception->get_response_object(1001);
		}
	} else {
		$return_object = $exception->get_response_object(1001);
	}

	echo json_encode($return_object);

	/**
	 * 用户登出
	 */
	function logout_user() {
		unset($_SESSION['userinfo']);

		$returnObject = (object) array(
			"result" => "success"
		);

		return $returnObject;
	}

	/**
	 * 检查要上传的文件是否存在
	 * 
	 * @param string $package_name app 包名
	 * @param string $filename 要上传的文件名
	 * @param string $sub_folder 要上传文件的分类子目录
	 */
	function check_upload_file($package_name, $filename, $sub_folder) {
		global $exception, $mysqli;

		if (empty($package_name)) {return $exception->get_response_object(2003);}
		if (empty($filename)) {return $exception->get_response_object(2020);}
		if (empty($sub_folder)) {return $exception->get_response_object(2021);}

		// 检查 package_name 是否存在
		$stmt = $mysqli->prepare(query_list_online_upate_manager::query_check_package_name);
		$stmt->bind_param('s', $package_name);
		$stmt->execute();
		$stmt->store_result();

		if ($stmt->num_rows() === 0) {return $exception->get_response_object(2014);}

		// 检查文件是否上传过
		$upload_path = _get_upload_path($package_name, $sub_folder);
		$upload_file_abs = $upload_path['upload_path_abs'] . $filename;

		$stmt = $mysqli->prepare(query_list_online_upate_manager::query_app_information_exist);
		$stmt->bind_param('ss', $upload_path['upload_path'], $filename);
		$stmt->execute();
		$stmt->store_result();

		// 如果上传过文件则检查文件是否存在
		if ($stmt->num_rows() > 0) {
			if (is_file($upload_file_abs)) {
				return $exception->get_response_object(2017);
			}
		}

		$stmt->close();
		$mysqli->close();

		$return_object = (object) array(
			'result' => 'success'
		);

		return $return_object;
	}

	/**
	 * 分段上传 app 文件并在数据库记录相关信息
	 * 
	 * @param string $append_data 通过 fcup 组件提交的 app 相关信息
	 */
	function upload_app_apart($append_data) {
		global $exception, $mysqli;

		// 信息验证
		if (empty($append_data)) {return $exception->get_response_object(2022);}
		
		// 解包 app 信息
		$append_data = json_decode($append_data, true);
		$package_name = $append_data['package_name'];
		$sub_folder = $append_data['sub_folder'];
		$app_version = $append_data['app_version'];
		$app_note = $append_data['app_note'];
		$is_apk = $append_data['is_apk'];
		$is_wgt = $append_data['is_wgt'];
		$is_force_update = $append_data['is_force_update'];
		$size = $_POST['file_size'];
		$filename = $_POST['file_name'];
		$type = $_POST['file_type'];

		if (empty($package_name)) {return $exception->get_response_object(2003);}
		if (empty($sub_folder)) {return $exception->get_response_object(2021);}
		if (empty($app_version)) {return $exception->get_response_object(2006);}
		if (empty($app_note)) {return $exception->get_response_object(2007);}
		if (gettype($is_apk) !== 'boolean') {return $exception->get_response_object(2008);}
		if (gettype($is_wgt) !== 'boolean') {return $exception->get_response_object(2009);}
		if (gettype($is_force_update) !== 'boolean') {return $exception->get_response_object(2010);}

		// 检查保存文件的绝对路径是否存，如有必要则创建文件夹
		$upload_path = _get_upload_path($package_name, $sub_folder);

		if (!is_dir($upload_path['upload_path_abs'])) {
			if (!mkdir($upload_path['upload_path_abs'], 0777, true)) {return $exception->get_response_object(2012);}
		}

		include_once('upload_file.php');

		$return_object = upload_file($upload_path['upload_path_abs']);

		if ($return_object['status'] === 2) {
			// 数据库添加 app 相关信息
			$stmt = $mysqli->prepare(query_list_online_upate_manager::query_append_app);
			$stmt->bind_param('sssssdsiii',
				$package_name,
				$app_version,
				$app_note,
				$filename,
				$upload_path['upload_path'],
				$size,
				$type,
				$is_apk,
				$is_wgt,
				$is_force_update
			);
			$stmt->execute();
			$stmt->close();
			$mysqli->close();
		}

		return $return_object;
	}

	/**
	 * 删除指定 app 的记录以及存储的文件
	 * 
	 * @param string $package_name app 包名
	 * @param boolean $include_file 是否同时删除存储的文件
	 * @param array $row_data 要删除的 app 的详细信息
	 */
	function remove_app($package_name, $include_file, $row_data) {
		global $exception, $mysqli;

		$include_file = is_numeric($include_file) ? (boolean) $include_file : '';

		if (empty($package_name)) {return $exception->get_response_object(2003);}
		if (gettype($include_file) !== 'boolean') {return $exception->get_response_object(2026);}

		// 解包 app 信息
		$id = $row_data['id'];
		$filename = $row_data['filename'];
		$version = $row_data['version'];
		$size = $row_data['size'];
		$is_apk = $row_data['is_apk'];
		$is_apk = is_numeric($is_apk) ? (boolean) $is_apk : '';
		$is_wgt = $row_data['is_wgt'];
		$is_wgt = is_numeric($is_wgt) ? (boolean) $is_wgt : '';
		$is_force_update = $row_data['is_force_update'];
		$is_force_update = is_numeric($is_force_update) ? (boolean) $is_force_update : '';
		$date = $row_data['date'];

		// 信息验证
		if (empty($id)) {return $exception->get_response_object(2023);}
		if (empty($filename)) {return $exception->get_response_object(2020);}
		if (empty($size)) {return $exception->get_response_object(2024);}
		if (empty($date)) {return $exception->get_response_object(2025);}
		if (empty($version)) {return $exception->get_response_object(2006);}
		if (gettype($is_apk) !== 'boolean') {return $exception->get_response_object(2008);}
		if (gettype($is_wgt) !== 'boolean') {return $exception->get_response_object(2009);}
		if (gettype($is_force_update) != 'boolean') {return $exception->get_response_object(2010);}

		// 尝试删除 app 文件
		$result_msg = '';

		if ($include_file) {
			$stmt = $mysqli->prepare(query_list_online_upate_manager::query_get_app_file_path);
			$stmt->bind_param('is', $id, $package_name);
			$stmt->execute();
			$stmt->store_result();
	
			if ($stmt->num_rows() === 0) {return $exception->get_response_object(2027);}
			
			$stmt->bind_result($path);
			$stmt->fetch();

			$document_root = "{$_SERVER['DOCUMENT_ROOT']}";
			$result_msg .= @unlink($document_root . $path . $filename) ? '删除文件成功，' : '删除文件失败，';
		}

		// 删除 app 记录信息
		$stmt = $mysqli->prepare(query_list_online_upate_manager::query_remove_app);
		$stmt->bind_param('isssdiiis', $id, $package_name, $version, $filename, $size, $is_apk, $is_wgt, $is_force_update, $date);
		$stmt->execute();

		$result_msg .= $mysqli->affected_rows >= 0 ? '删除记录成功' : '删除记录失败';

		$stmt->close();
		$mysqli->close();

		$return_object = (object) array(
			'result' => 'success',
			'result_msg' => $result_msg
		);

		return $return_object;
	}

	/**
	 * 上传 app 文件并在数据库记录相关信息
	 * 
	 * @param string $params app 相关信息 json 字符串
	 * @param object $upload_files 客户端上传的二进制文件
	 */
	function upload_app($params, $upload_files) {
		global $exception, $mysqli, $upload_folder;

		if (empty($params)) {return $exception->get_response_object(2016);}

		// 解包 app 信息
		$params = json_decode($params, true);
		$package_name = $params['package_name'];
		$app_version = $params['app_version'];
		$app_note = $params['app_note'];
		$is_apk = $params['is_apk'];
		$is_wgt = $params['is_wgt'];
		$is_force_update = $params['is_force_update'];

		// 信息验证
		if (empty($package_name)) {return $exception->get_response_object(2003);}
		if (empty($upload_files)) {return $exception->get_response_object(2005);}
		if (empty($app_version)) {return $exception->get_response_object(2006);}
		if (empty($app_note)) {return $exception->get_response_object(2007);}
		if (gettype($is_apk) !== 'boolean') {return $exception->get_response_object(2008);}
		if (gettype($is_wgt) !== 'boolean') {return $exception->get_response_object(2009);}
		if (gettype($is_force_update) !== 'boolean') {return $exception->get_response_object(2010);}

		// 检查 package_name 是否存在
		$stmt = $mysqli->prepare(query_list_online_upate_manager::query_check_package_name);
		$stmt->bind_param('s', $package_name);
		$stmt->execute();
		$stmt->store_result();

		if ($stmt->num_rows() === 0) {return $exception->get_response_object(2014);}

		// 获取上传文件信息
		$upload_file = $upload_files['upload_file'];

		// 检查文件是否合法上传
		if (!is_uploaded_file($upload_file['tmp_name'])) {return $exception->get_response_object(2011);}

		// 检查资源文件类型是否合法
		$upload_subpath = $is_apk ? 'apk' : ($is_wgt ? 'wgt' : '');

		if (empty($upload_subpath)) {return $exception->get_response_object(2015);}

		// 检查文件是否上传过
		$document_root = "{$_SERVER['DOCUMENT_ROOT']}";
		$upload_path = strstr($_SERVER['REQUEST_URI'], '/v1/', true) . "{$upload_folder}/{$package_name}/{$upload_subpath}/";
		$upload_path_abs = $document_root . $upload_path;
		$upload_file_abs = $upload_path_abs . $upload_file['name'];

		$stmt = $mysqli->prepare(query_list_online_upate_manager::query_app_information_exist);
		$stmt->bind_param('ss', $upload_path, $upload_file['name']);
		$stmt->execute();
		$stmt->store_result();

		// 如果上传过文件则检查文件是否存在，如果不存在则继续上传
		if ($stmt->num_rows() > 0) {
			if (is_file($upload_file_abs)) {
				return $exception->get_response_object(2017);
			}
		}

		// 检查保存文件的绝对路径是否存，如有必要则创建文件夹
		if (!is_dir($upload_path_abs)) {
			if (!mkdir($upload_path_abs, 0777, true)) {return $exception->get_response_object(2012);}
		}

		// 将文件保存到指定的绝对路径
		if (!move_uploaded_file($upload_file['tmp_name'], $upload_file_abs)) {
			return $exception->get_response_object(2013);
		}

		// 数据库添加 app 相关信息
		$stmt = $mysqli->prepare(query_list_online_upate_manager::query_append_app);
		$stmt->bind_param('sssssdsiii',
			$package_name,
			$app_version,
			$app_note,
			$upload_file['name'],
			$upload_path,
			$upload_file['size'],
			$upload_file['type'],
			$is_apk,
			$is_wgt,
			$is_force_update
		);
		$stmt->execute();
		$stmt->close();
		$mysqli->close();

		$return_object = (object) array(
			'result' => 'success'
		);

		return $return_object;
	}

	/**
	 * 检查 app 更新信息
	 * 
	 * @param string $package_name app 包名
	 * @param string $app_version app 当前版本
	 * @param boolean $get_last_version 是否获取最新版信息
	 */
	function check_update($package_name, $app_version, $get_last_version) {
		global $exception, $mysqli;

		if (empty($package_name)) {return $exception->get_response_object(2003);}
		if (empty($app_version)) {return $exception->get_response_object(2006);}
		if (empty($get_last_version) || gettype($get_last_version) !== 'string') {
			$get_last_version = true;
		} else {
			$get_last_version = $get_last_version === 'true' ? true : false;
		}

		$return_object = (object) array(
			'result' => 'success',
			'status' => 0
		);

		if ($get_last_version) {
			// 获取指定 package_name 最新一条记录
			$stmt = $mysqli->prepare(query_list_online_upate_manager::query_get_last_app_information);
			$stmt->bind_param('s', $package_name);
			$stmt->execute();
			$stmt->store_result();
		} else {
			// 获取指定 package_name 最新一条记录
			$stmt = $mysqli->prepare(query_list_online_upate_manager::query_get_specified_app_information);
			$stmt->bind_param('ss', $package_name, $app_version);
			$stmt->execute();
			$stmt->store_result();
		}

		// 如果没有记录表示没有可用更新
		if ($stmt->num_rows() === 0) {return $return_object;}

		$stmt->bind_result($version, $note, $filename, $path, $size, $is_apk, $is_wgt, $is_force_update, $date);
		$stmt->fetch();
		$stmt->close();
		$mysqli->close();

		// 检查用户当前版本号和记录中的版本号是否一致
		// 这里只是简单的相等判断，不比较版本号数字
		if ($get_last_version) {
			if ($version === $app_version) {return $return_object;}
		}

		// 检查资源文件是否存在
		if (!is_file("{$_SERVER['DOCUMENT_ROOT']}{$path}{$filename}")) {return $exception->get_response_object(2019);}

		// 检查资源信息是否合法
		$update_status = $is_apk ? 1 : ($is_wgt ? 2 : -1);

		if ($update_status === -1) {return $exception->get_response_object(2018);}

		$url_prefix = (!empty($_SERVER['HTTPS']) ? 'https' : 'http') . "://{$_SERVER['HTTP_HOST']}";

		// 返回 app 更新信息
		$return_object = (object) array(
			'result' => 'success',
			'status' => $update_status,
			'version' => $version,
			'note' => $note,
			'size' => $size,
			'force_update' => $is_force_update,
			'date' => $date,
			'url' => "{$url_prefix}{$path}{$filename}"
		);

		return $return_object;
	}

	/**
	 * 获取指定 package_name 的所有 app 信息
	 * 用于前端页面 DataTable 刷新表格内容
	 * 
	 * @param string $package_name app 包名
	 */
	function load_apps($package_name) {
		global $mysqli;

		if (empty($package_name)) {return array('apps' => array());}

		$stmt = $mysqli->prepare(query_list_online_upate_manager::query_get_all_apps);
		$stmt->bind_param('s', $package_name);
		$stmt->execute();
		$stmt->store_result();
		$stmt->bind_result($id, $version, $filename, $size, $is_apk, $is_wgt, $is_force_update, $date);

		$apps = array();

		while ($stmt->fetch()) {
			$app = array(
				'id' => $id,
				'version' => $version,
				'filename' => $filename,
				'size' => $size,
				'is_apk' => $is_apk,
				'is_wgt' => $is_wgt,
				'is_force_update' => $is_force_update,
				'date' => $date
			);

			$apps[] = $app;
		}

		$return_object = (object) array(
			'result' => 'success',
			'apps' => $apps
		);

		$stmt->close();
		$mysqli->close();

		return $return_object;
	}

	/**
	 * 获取所有 package_name
	 * 用于前端页面下拉列表刷新选项
	 */
	function load_packages() {
		global $exception, $mysqli;

		$stmt = $mysqli->prepare(query_list_online_upate_manager::query_get_all_packages);
		$stmt->execute();
		$stmt->store_result();
		$stmt->bind_result($package);

		$packages = array();
		while ($stmt->fetch()) {
			$packages[] = $package;
		}

		$return_object = (object) array(
			'result' => 'success',
			'packages' => $packages
		);

		$stmt->close();
		$mysqli->close();

		return $return_object;
	}

	/**
	 * 删除指定的 package_name
	 * 
	 * @param string $package_name app 包名
	 */
	function remove_package($package_name) {
		global $exception, $mysqli;

		if (empty($package_name)) {return $exception->get_response_object(2003);}

		$stmt = $mysqli->prepare(query_list_online_upate_manager::query_remove_package);
		$stmt->bind_param('s', $package_name);
		$stmt->execute();
		$stmt->close();
		$mysqli->close();

		$return_object = (object) array(
			'result' => 'success'
		);

		return $return_object;
	}

	/**
	 * 添加 package_name
	 * 
	 * @param string $package_name app 包名
	 */
	function append_package($package_name) {
		global $exception, $mysqli;

		if (empty($package_name)) {return $exception->get_response_object(2003);}

		// 检查 package_name 是否存在
		$stmt = $mysqli->prepare(query_list_online_upate_manager::query_check_package_name);
		$stmt->bind_param('s', $package_name);
		$stmt->execute();
		$stmt->store_result();

		if ($stmt->num_rows() > 0) {return $exception->get_response_object(2004);}

		// 数据库插入 package_name 数据
		$stmt = $mysqli->prepare(query_list_online_upate_manager::query_append_package);
		$stmt->bind_param('s', $package_name);
		$stmt->execute();
		$stmt->close();
		$mysqli->close();

		$return_object = (object) array(
			'result' => 'success'
		);

		return $return_object;
	}

	/**
	 * 用户登录验证
	 * 
	 * @param string $username 用户名
	 * @param string $password 密码
	 */
	function signin_user($username, $password) {
		global $exception, $mysqli;

		if (empty($username)) {return $exception->get_response_object(2000);}
		if (empty($password)) {return $exception->get_response_object(2001);}

		$stmt = $mysqli->prepare(query_list_online_upate_manager::query_signin_user);
		$stmt->bind_param('ss', $username, $password);
		$stmt->execute();
		$stmt->store_result();

		if ($stmt->num_rows() == 0) {return $exception->get_response_object(2002);}

		// 登录成功后将用户信息写入缓存
		$_SESSION['userinfo'] = array(
			'username' => $username,
			'signature' => md5(sha1(md5($username) . $password)),
			'origin' => $_SERVER['REDIRECT_HTTP_ORIGIN']
		);

		$return_object = (object) array(
			'result' => 'success'
		);

		$stmt->close();
		$mysqli->close();

		return $return_object;
	}


	/**
	 * 获取用户上传文件路径，包括 相对路径 和 绝对路径
	 * 
	 * 路径组成: /upload_folder/package_name/sub_folder/
	 * 例如: /upload/wang.walkline.remotewol/apk
	 * 
	 * @param string $package_name app 包名
	 * @param string $sub_folder app 子目录
	 */
	function _get_upload_path($package_name, $sub_folder) {
		global $upload_folder;

		$document_root = "{$_SERVER['DOCUMENT_ROOT']}";
		$upload_path = strstr($_SERVER['REQUEST_URI'], '/v1/', true) . "{$upload_folder}/{$package_name}/{$sub_folder}/";
		$upload_path_abs = $document_root . $upload_path;

		$return_object = array(
			'upload_path' => $upload_path,
			'upload_path_abs' => $upload_path_abs
		);

		return $return_object;
	}

	/**
	 * 检查访问接口的用户是否已授权
	 */
	function _validate_user_authorize() {
		global $exception, $mysqli;

		if (!isset($_SESSION['userinfo']) ||
			!isset($_SESSION['userinfo']['username']) ||
			!isset($_SESSION['userinfo']['signature']) ||
			!isset($_SESSION['userinfo']['origin'])) {
			// return $exception->get_response_object(1002);
			return false;
		}

		$stmt = $mysqli->prepare(query_list_online_upate_manager::query_user_information);
		$stmt->execute();
		$stmt->store_result();

		if ($stmt->num_rows() == 0) {
			// return $exception->get_response_object(1003);
			return false;
		}

		$stmt->bind_result($username, $password);
		$stmt->fetch();
		$stmt->close();

		if ($_SESSION['userinfo']['username'] !== $username ||
			$_SESSION['userinfo']['signature'] !== md5(sha1(md5($username) . $password)) ||
			$_SESSION['userinfo']['origin'] !== $_SERVER['REDIRECT_HTTP_ORIGIN']) {
			// return $exception->get_response_object(1002);
			return false;
		}

		return true;
	}
?>